#include <windows.h>
#include <tchar.h>

#include <sql.h>
#include <sqltypes.h>
#include <sqlext.h>
#include <string>
#include <vector>
#include <iostream>

using namespace std;

int test(SQLTCHAR* szConnectionString, size_t testSizeInBytes)
{
	SQLLEN len = testSizeInBytes;


	SQLHENV henv;
	SQLHDBC hdbc;
	SQLHSTMT hstmt;
	SQLRETURN rc;

	SQLTCHAR* InConnStr = szConnectionString;
	SQLSMALLINT InConnStrLen = _tcslen(InConnStr);

	SQLTCHAR OutConnStr[255];
	SQLSMALLINT OutConnStrLen = 255;

	// Allocate environment handle
	rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
	{
		cout << "error: Allocate environment handle, rc=" << rc << endl;
		return 1;
	}

	// Set the ODBC version environment attribute
	rc = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER*)SQL_OV_ODBC3, 0); 
	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
	{
		cout << "error: Set the ODBC version environment attribute, rc=" << rc << endl;
		return 1;
	}

	// Allocate connection handle
	rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc); 
	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
	{
		cout << "error: Allocate connection handle, rc=" << rc << endl;
		return 1;
	}

	// Set login timeout to 15 seconds
	rc = SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)15, 0);
	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
	{
		cout << "error: Set login timeout to 15 seconds, rc=" << rc << endl;
		return 1;
	}

	// Connect to HANA ODBC driver
	rc = SQLDriverConnect(
		hdbc, 
		NULL, 
		InConnStr, 
		InConnStrLen,
		OutConnStr,
		OutConnStrLen, 
		&OutConnStrLen,
		SQL_DRIVER_NOPROMPT );
	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
	{
		cout << "error: Connect to HANA ODBC driver, rc=" << rc << endl;
		return 1;
	}

	// Allocate statement handle
	rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt); 
	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
	{
		cout << "error: Allocate statement handle, rc=" << rc << endl;
		return 1;
	}



	// SET SCHEMA MILES_TEST
	rc = SQLExecDirect(hstmt, _T("SET SCHEMA MILES_TEST"), SQL_NTS);
	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
	{
		cout << "error: SET SCHEMA MILES_TEST, rc=" << rc << endl;
		return 1;
	}

	// SQLPrepare->CALL MTBLOB(?)
	rc = SQLPrepare(hstmt, _T("CALL MTBLOB(?)"), SQL_NTS);
	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
	{
		cout << "error: SQLPrepare->CALL MTBLOB(?), rc=" << rc << endl;
		return 1;
	}

	// Set parameter
	char *buffer = new char[len];
	for (int i = 0; i < 26; i++)
	{
		buffer[i] = 'A' + i; // set the first 26 chars to ABCD...
	}
	rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, SQL_DESC_LENGTH, 0,
		buffer, len, &len);
	if (rc != SQL_SUCCESS)
	{
		cout << "error: Set parameter, rc=" << rc << endl;
		return 1;
	}

	// SQLExecute->CALL MTBLOB(?)
	rc = SQLExecute (hstmt);
	if (rc != SQL_SUCCESS)
	{
		cout << "error: SQLExecute->CALL MTBLOB(?), rc=" << rc << endl;
		return 1;
	}

	// Retrieve result
	while(SQLFetch(hstmt) == SQL_SUCCESS)
	{
		char *data = new char[len];

		rc = SQLGetData(hstmt, 1, SQL_BINARY, data, len, &len);
		if (rc != SQL_SUCCESS)
		{
			cout << "error: SQLGetData, rc=" << rc << endl;
			return 1;
		}

		int ret = memcmp(buffer, data, len);
		cout << "ret = " << ret << endl;
		delete[] data;
	}

	// Free
	delete[] buffer;
	rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
	SQLDisconnect(hdbc);
	SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
	SQLFreeHandle(SQL_HANDLE_ENV, henv);
	cout << "done." << endl;

	return 0;
}

int main()
{
	// change connection string if needed, note HDBODBC refers to 64 bit ODBC driver
	SQLTCHAR InConnStr[] = _T("driver={HDBODBC};servernode=10.58.136.174:30515;uid=SYSTEM;pwd=manager");

	//size_t testSize = 1 * 1000 * 1024; // 1000K works fine!
	size_t testSize = 1 * 1024 * 1024; // 1M doesn't work!!

	return test(InConnStr, testSize);
}

